from pathlib import Path
from typing import Union

def get_project_root() -> Path:
    """
    获取项目根目录的绝对路径（核心函数）
    原理：以当前工具文件（path_utils.py）为基准，向上追溯到项目根目录
    （需根据实际目录结构调整，默认工具文件在 tools/ 目录，根目录是 tools 的上一级）
    """
    # 1. 获取当前工具文件（path_utils.py）的绝对路径
    current_file = Path(__file__).resolve()
    # 2. 向上追溯到项目根目录（根据实际结构调整 parent 层级）
    # 示例：若 path_utils.py 在 "项目根目录/tools/" 下，根目录 = current_file.parent.parent
    project_root = current_file.parent.parent
    # 3. 验证根目录（可选：确保根目录下有标志性文件/文件夹，如 data/、core/）
    if not (project_root / "data").exists() and not (project_root / "core").exists():
        raise FileNotFoundError(
            f"未识别到项目根目录！当前计算的根目录：{project_root}\n"
            "请检查 path_utils.py 的位置，调整 parent 层级（如 current_file.parent.parent.parent）"
        )
    return project_root

def get_absolute_path(relative_path: Union[str, Path]) -> Path:
    """
    计算目标文件的绝对路径（基于项目根目录）
    :param relative_path: 相对于项目根目录的路径（如 "data/page_config/login_page_backup.yaml"）
    :return: 目标文件的绝对 Path 对象
    """
    # 1. 获取项目根目录
    project_root = get_project_root()
    # 2. 拼接绝对路径（pathlib 自动处理跨平台路径分隔符）
    absolute_path = project_root / relative_path
    # 3. 规范化路径（解决 "../" 等相对路径符号）
    return absolute_path.resolve()

def check_file_exists(file_path: Union[str, Path]) -> None:
    """
    检查文件是否存在，不存在则抛出异常（用于提前拦截路径错误）
    :param file_path: 可以是相对路径（自动转为绝对路径）或绝对路径
    """
    # 若传入相对路径，先转为绝对路径
    abs_path = get_absolute_path(file_path) if not Path(file_path).is_absolute() else Path(file_path)
    if not abs_path.exists():
        raise FileNotFoundError(f"文件不存在：{abs_path}")
    if not abs_path.is_file():
        raise IsADirectoryError(f"路径不是文件：{abs_path}")